/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - RMI Client, FileChooser and WebDAV
 */
package de.ipk_gatersleben.bit.bi.edal.rmi.client.gui.editor;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.swing.AbstractAction;
import javax.swing.AbstractListModel;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListModel;
import javax.swing.SwingConstants;

import de.ipk_gatersleben.bit.bi.edal.aspectj.security.GrantableMethods;
import de.ipk_gatersleben.bit.bi.edal.aspectj.security.GrantableMethods.Methods;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientDataManager;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.ClientPrimaryDataEntity;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.util.PrincipalUtil;
import de.ipk_gatersleben.bit.bi.edal.rmi.client.util.StackTraceUtil;

/**
 * <code>PermissionNewuserDialog</code> provides a mechanism for the user to add
 * new users and manage there permissions of EDAL File System.
 * <p>
 * 
 * The following code pops up a PermissionNewuserDialog for the user's directory
 * 
 * <pre>
 * PermissionNewuserDialog permission = new PermissionNewuserDialog(directory,
 * 		client);
 * permission.showOpenDialog();
 * </pre>
 * <p>
 * 
 * @version 1.0
 * @author Jinbo Chen
 * 
 */
public class PermissionNewuserDialog extends JDialog {
	private static final long serialVersionUID = 1L;

	/**
	 * Return value if cancel is chosen.
	 */
	public static final int CANCEL_OPTION = 1;
	/**
	 * Return value if approve (yes, ok) is chosen.
	 */
	public static final int APPROVE_OPTION = 0;

	private static final String DEFAULT_SOURCE_CHOICE_LABEL = "Available Permissions";
	private static final String DEFAULT_DEST_CHOICE_LABEL = "Assigned Permissions";
	private static final String ADD_BUTTON_LABEL = "Add >>";
	private static final String REMOVE_BUTTON_LABEL = "<< Remove";
	private static final Insets EMPTY_INSETS = new Insets(0, 0, 0, 0);

	private JButton savebtn;
	private JButton cancelbtn;

	public int returnvalue;

	private JList sourceList;
	private JList destList;
	private SortedListModel sourceListModel;
	private SortedListModel destListModel;
	private JButton removeButton;
	private JLabel destLabel;

	private ClientPrimaryDataEntity dataentry = null;
	private ClientDataManager client;
	private List<Methods> allavailablemethods = new ArrayList<Methods>();
	private List<String> principals = new ArrayList<String>();
	private List<Class<? extends Principal>> supportedprincipals;
	private JTextField usertext = new JTextField();
	private JComboBox principalcomboBox;

	/**
	 * Constructs a <code>PermissionNewuserDialog</code> that is initialized
	 * with <code>dataentry</code> as the EDAL dataentry, and
	 * <code>client</code> as the rmi client datamanager. If any of the
	 * parameters are <code>null</code> this method will not initialize.
	 * 
	 * @param dataentry
	 *            the EDAL dataentry
	 * @param client
	 *            the rmi client datamanager
	 */
	public PermissionNewuserDialog(ClientPrimaryDataEntity dataentry,
			ClientDataManager client) {
		this.dataentry = dataentry;
		this.client = client;
		loadconstants();

		setTitle("Add NewUser");

		JPanel contents = (JPanel) getContentPane();
		contents.setLayout(new BorderLayout());

		JPanel userselectpanel = new JPanel();

		JLabel principallabel = new JLabel("UserName:", SwingConstants.CENTER);
		userselectpanel.add(principallabel);

		usertext.setPreferredSize(new Dimension(90, 28));

		userselectpanel.add(usertext);

		principalcomboBox = new JComboBox();
		principalcomboBox.setName("principal");
		for (Class<? extends Principal> obj : supportedprincipals) {
			principalcomboBox.addItem(obj.getSimpleName());
			principals.add(obj.getName());
		}

		userselectpanel.add(principalcomboBox);

		JPanel permissioneditpanel = new JPanel();
		initpermissionpanel(permissioneditpanel);

		JPanel editPane = new JPanel();
		editPane.setLayout(new BorderLayout());
		editPane.add(userselectpanel, BorderLayout.NORTH);
		editPane.add(permissioneditpanel, BorderLayout.CENTER);

		contents.add(editPane, BorderLayout.CENTER);
		contents.add(createbuttonpanel(), BorderLayout.SOUTH);

		this.setSize(new Dimension(600, (int) (600 * 0.618)));
	}

	/**
	 * pop up a PermissionNewuserDialog Dialog
	 */
	public int showOpenDialog() {
		setModal(true);
		setLocationRelativeTo(null);
		setVisible(true);
		return returnvalue;
	}

	private void initpermissionpanel(JPanel permissioneditpanel) {
		permissioneditpanel.setBorder(BorderFactory.createEtchedBorder());
		permissioneditpanel.setLayout(new GridBagLayout());
		JLabel sourceLabel = new JLabel(DEFAULT_SOURCE_CHOICE_LABEL);
		sourceListModel = new SortedListModel();
		sourceList = new JList(sourceListModel);
		permissioneditpanel.add(sourceLabel, new GridBagConstraints(0, 0, 1, 1,
				0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				EMPTY_INSETS, 0, 0));
		permissioneditpanel.add(new JScrollPane(sourceList),
				new GridBagConstraints(0, 1, 1, 5, .5, 1,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						EMPTY_INSETS, 0, 0));

		JButton addButton = new JButton(ADD_BUTTON_LABEL);
		permissioneditpanel.add(addButton, new GridBagConstraints(1, 2, 1, 2,
				0, .25, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				EMPTY_INSETS, 0, 0));
		addButton.addActionListener(new AddListener());
		removeButton = new JButton(REMOVE_BUTTON_LABEL);
		permissioneditpanel.add(removeButton, new GridBagConstraints(1, 4, 1,
				2, 0, .25, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				new Insets(0, 5, 0, 5), 0, 0));
		removeButton.addActionListener(new RemoveListener());

		destLabel = new JLabel(DEFAULT_DEST_CHOICE_LABEL);
		destListModel = new SortedListModel();
		destList = new JList(destListModel);
		permissioneditpanel.add(destLabel, new GridBagConstraints(2, 0, 1, 1,
				0, 0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
				EMPTY_INSETS, 0, 0));
		permissioneditpanel.add(new JScrollPane(destList),
				new GridBagConstraints(2, 1, 1, 5, .5, 1.0,
						GridBagConstraints.CENTER, GridBagConstraints.BOTH,
						EMPTY_INSETS, 0, 0));

		refreshPrincipals();
	}

	private void refreshPrincipals() {
		List<Methods> allavailablenoselectedmethods = new ArrayList<Methods>();

		for (Methods method : allavailablemethods) {
			allavailablenoselectedmethods.add(method);
		}

		addSourceElements(allavailablenoselectedmethods.toArray(new Methods[0]));
	}

	private void loadconstants() {
		try {
			if (dataentry != null) {
				List<Methods> commonskeysets = GrantableMethods.ENTITY_METHODS;
				for (GrantableMethods.Methods m : commonskeysets) {
					allavailablemethods.add(m);
				}

				if (dataentry.isDirectory()) {
					List<Methods> keysets = GrantableMethods.DIRECTORY_METHODS;
					for (GrantableMethods.Methods m : keysets) {
						allavailablemethods.add(m);
					}
				} else {
					List<Methods> keysets = GrantableMethods.FILE_METHODS;
					for (GrantableMethods.Methods m : keysets) {
						allavailablemethods.add(m);
					}
				}
			}

			supportedprincipals = client.getSupportedPrincipals();
		} catch (RemoteException e) {
			ClientDataManager.logger.error(StackTraceUtil.getStackTrace(e));
			JOptionPane.showMessageDialog(
					null,
					"Call remote Edal server function exception:"
							+ e.getMessage(), "EdalFileChooser",
					JOptionPane.ERROR_MESSAGE);
		} catch (EdalException e) {
			ClientDataManager.logger.error(StackTraceUtil.getStackTrace(e));
			JOptionPane.showMessageDialog(null,
					"Call Edal server function exception:" + e.getMessage(),
					"EdalFileChooser", JOptionPane.ERROR_MESSAGE);
		}
	}

	private void fillListModel(SortedListModel model, ListModel newValues) {
		int size = newValues.getSize();
		for (int i = 0; i < size; i++) {
			model.add(newValues.getElementAt(i));
		}
	}

	private void fillListModel(SortedListModel model, Object newValues[]) {
		model.addAll(newValues);
	}

	public void addDestinationElements(Object newValue[]) {
		fillListModel(destListModel, newValue);
	}

	public void addSourceElements(ListModel newValue) {
		fillListModel(sourceListModel, newValue);
	}

	public void addSourceElements(Object newValue[]) {
		fillListModel(sourceListModel, newValue);
	}

	public void clearSourceListModel() {
		sourceListModel.clear();
	}

	public void clearDestinationListModel() {
		destListModel.clear();
	}

	private void clearSourceSelected() {
		Object selected[] = sourceList.getSelectedValues();
		for (int i = selected.length - 1; i >= 0; --i) {
			sourceListModel.removeElement(selected[i]);
		}
		sourceList.getSelectionModel().clearSelection();
	}

	private void clearDestinationSelected() {
		Object selected[] = destList.getSelectedValues();
		for (int i = selected.length - 1; i >= 0; --i) {
			destListModel.removeElement(selected[i]);
		}
		destList.getSelectionModel().clearSelection();
	}

	private class AddListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			Object selected[] = sourceList.getSelectedValues();
			addDestinationElements(selected);
			clearSourceSelected();
		}
	}

	private class RemoveListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			Object selected[] = destList.getSelectedValues();
			addSourceElements(selected);
			clearDestinationSelected();
		}
	}

	private JPanel createbuttonpanel() {
		savebtn = new JButton(okAction);
		cancelbtn = new JButton(cancelAction);

		JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 5));
		buttonPane.add(savebtn);
		buttonPane.add(cancelbtn);

		return buttonPane;
	}

	private void assignpermission() {
		// get principal
		String currentuser = usertext.getText().trim();
		if (currentuser.length() == 0) {
			JOptionPane.showMessageDialog(null, "Please Input UserName!",
					"EdalFileChooser", JOptionPane.ERROR_MESSAGE);
		}
		String currentprincipalname = principals.get(principalcomboBox
				.getSelectedIndex());
		Principal currentprincipal = PrincipalUtil.getInstance(
				currentprincipalname, currentuser);

		// grant permissions to user
		int destsize = destListModel.getSize();
		for (int i = 0; i < destsize; i++) {
			try {
				boolean isin = PrincipalUtil.checkPermission(dataentry,
						currentprincipalname, currentuser, destListModel
								.getElementAt(i).toString());
				if (!isin) {
					dataentry.grantPermission(currentprincipal, Methods
							.valueOf(destListModel.getElementAt(i).toString()));
				}
			} catch (RemoteException ex) {
				ClientDataManager.logger
						.error(StackTraceUtil.getStackTrace(ex));
				JOptionPane.showMessageDialog(
						null,
						"Call remote Edal server function exception:"
								+ ex.getMessage(), "EdalFileChooser",
						JOptionPane.ERROR_MESSAGE);
			} catch (Exception ex) {
				ClientDataManager.logger
						.error(StackTraceUtil.getStackTrace(ex));
				JOptionPane.showMessageDialog(
						null,
						"Call Edal server function exception:"
								+ ex.getMessage(), "EdalFileChooser",
						JOptionPane.ERROR_MESSAGE);
			}
		}
		JOptionPane.showMessageDialog(null, "Grant permissions finish!",
				"EdalFileChooser", JOptionPane.INFORMATION_MESSAGE);
	}

	private Action okAction = new AbstractAction("Ok") {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			assignpermission();
			returnvalue = APPROVE_OPTION;
			dispose();
		}
	};

	private Action cancelAction = new AbstractAction("Cancel") {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			returnvalue = CANCEL_OPTION;
			dispose();
		}
	};
}

class SortedListModel extends AbstractListModel {

	SortedSet model;

	public SortedListModel() {
		model = new TreeSet();
	}

	public int getSize() {
		return model.size();
	}

	public Object getElementAt(int index) {
		return model.toArray()[index];
	}

	public void add(Object element) {
		if (model.add(element)) {
			fireContentsChanged(this, 0, getSize());
		}
	}

	public void addAll(Object elements[]) {
		Collection c = Arrays.asList(elements);
		model.addAll(c);
		fireContentsChanged(this, 0, getSize());
	}

	public void clear() {
		model.clear();
		fireContentsChanged(this, 0, getSize());
	}

	public boolean contains(Object element) {
		return model.contains(element);
	}

	public Object firstElement() {
		return model.first();
	}

	public Iterator iterator() {
		return model.iterator();
	}

	public Object lastElement() {
		return model.last();
	}

	public boolean removeElement(Object element) {
		boolean removed = model.remove(element);
		if (removed) {
			fireContentsChanged(this, 0, getSize());
		}
		return removed;
	}
}